#include "plugin_manager.h"

#include <fmt/core.h>
#include <pubsub.h>
#include <stdlib.h>

#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QJsonValue>
#include <QList>
#include <QMutex>
#include <QPluginLoader>

PluginManager *PluginManager::plugin_manager_self = nullptr;

PluginManager::PluginManager() {
    this->log = LogUtil::getLogger("pluginManager");
    connect(Pubsub::getInstance(), &Pubsub::signalPublish, this, &PluginManager::subscribe);
}

PluginManager *PluginManager::getInstance() {
    if (plugin_manager_self == NULL) {
        static QMutex mutex;

        QMutexLocker locker(&mutex);

        if (plugin_manager_self == NULL) {
            plugin_manager_self = new PluginManager;
        }
    }

    return plugin_manager_self;
}

void PluginManager::loadPlugins(QString pluginPath) {
    auto pluginConfigPath = pluginPath + "/plugins.json";
    auto array = this->parsePluginsConfig(pluginConfigPath);
    if (array.isEmpty()) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置为空. {}", pluginConfigPath.toStdString()));
        return;
    }

    for (int i = 0; i < array.size(); i++) {
        QJsonObject o = array.at(i).toObject();
        // 如果插件没有启用, 则跳过
        if (!o.value("enable").toBool()) {
            continue;
        }
        // 加载插件
        SPDLOG_LOGGER_DEBUG(this->log, fmt::format("load plugin:{}.{}", o.value("id").toString().toStdString(),
                                                   o.value("version").toString().toStdString()));
        auto currPluginPath = pluginPath + "/" + o.value("path").toString();
        SPDLOG_LOGGER_DEBUG(this->log, fmt::format("plugin path:{}", currPluginPath.toStdString()));
        if (this->loadSinglePlugin(currPluginPath)) {
            SPDLOG_LOGGER_DEBUG(this->log,
                                fmt::format("load plugin:{}.{} success", o.value("id").toString().toStdString(),
                                            o.value("version").toString().toStdString()));
        } else {
            SPDLOG_LOGGER_DEBUG(this->log,
                                fmt::format("load plugin:{}.{} failed", o.value("id").toString().toStdString(),
                                            o.value("version").toString().toStdString()));
        }
    }
}

QJsonArray PluginManager::parsePluginsConfig(QString pluginConfigPath) {
    // 解析 plugins.json 文件, 获取插件信息
    QFile jsonFile(pluginConfigPath);
    if (!jsonFile.open(QFile::ReadOnly | QFile::Text)) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置文件打开失败. {}", pluginConfigPath.toStdString()));
        return QJsonArray();
    }
    QByteArray jsonData = jsonFile.readAll();
    jsonFile.close();

    QJsonParseError jsonParseError;
    QJsonDocument   document = QJsonDocument::fromJson(jsonData, &jsonParseError);
    if (document.isNull() || jsonParseError.error != QJsonParseError::NoError) {
        SPDLOG_LOGGER_ERROR(this->log, fmt::format("插件配置文件打开失败. {}", pluginConfigPath.toStdString()));
        return QJsonArray();
    }

    return document.isArray() ? document.array() : QJsonArray();
}

bool PluginManager::loadSinglePlugin(QString pluginPath) {
    JannaPluginInterface *currPlugin            = nullptr;
    bool                  currPluginLoadSuccess = false;
    // 加载插件
    QDir              pluginsDir(pluginPath);
    const QStringList entries = pluginsDir.entryList(QDir::Files);
    for (const QString &fileName : entries) {
        // 只处理 .so 文件
        if (!fileName.endsWith(".so")) {
            continue;
        }
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        QObject      *plugin = pluginLoader.instance();

        if (!plugin) {
            // TODO error log
            pluginLoader.unload();
            break;
        }

        currPlugin = qobject_cast<JannaPluginInterface *>(plugin);
        if (!currPlugin) {
            // TODO error log
            pluginLoader.unload();
            break;
        }

        if (!this->parseMetadata(currPlugin, pluginLoader.metaData())) {
            SPDLOG_LOGGER_ERROR(this->log,
                                fmt::format("插件元数据解析失败. 插件:{}", currPlugin->pluginName().toStdString()));
            pluginLoader.unload();
            break;
        }

        currPluginLoadSuccess = true;
        this->m_plugin_map.insert(currPlugin->pluginName(), currPlugin);
    }
    return currPluginLoadSuccess;
}
bool PluginManager::parseMetadata(JannaPluginInterface *currPlugin, QJsonObject metadata) {
    if (metadata.isEmpty()) {
        SPDLOG_LOGGER_ERROR(this->log,
                            fmt::format("插件元数据不存在. 插件:{}", currPlugin->pluginName().toStdString()));
        return false;
    }
    auto subscribeObject = metadata.value("MetaData").toObject().value("subscribe");
    if (subscribeObject.isNull() || !subscribeObject.isArray()) {
        SPDLOG_LOGGER_ERROR(
            this->log, fmt::format("插件元数据 subscribe 不存在. 插件:{}", currPlugin->pluginName().toStdString()));
        return false;
    }

    QList<JannaTopic> topics;
    auto              array = subscribeObject.toArray();
    for (auto i = 0; i < array.count(); i++) {
        auto       o = array.at(i).toObject();
        JannaTopic topic;
        topic.source  = o.value("source").toString();
        topic.topic   = o.value("topic").toString();
        topic.publish = o.value("publish").toString();

        topics.append(topic);
    }
    currPlugin->initPoints(topics);

    return true;
}

void PluginManager::subscribe(const JannaTopic &topic, const QMap<QString, QVariant> &param) {
    QMapIterator<QString, JannaPluginInterface *> it(this->m_plugin_map);
    while (it.hasNext()) {
        it.next();
        it.value()->pointHandlerInterceptor(topic, param);
    }
}
